import EmptyOption from './EmptyOption';
import { Value } from '../../inner/Value';
import { TagConfig, TagGroupProps } from '../../TagGroup';
import Dropdown from '../../Dropdown';
import { ComputedRef, PropType, VNode, computed, defineComponent, onMounted, provide, reactive, ref, unref, watch, watchEffect } from 'vue';
import InnerOption from './InnerOption';
import VirtualList from '../../virtual-list';
import formFieldRef from '../../use/formFieldRef';
import { FeatherChevronDown } from 'cui-vue-icons/feather';
export {default as Option} from './Option';
export {default as OptionGroup} from './OptionGroup';

type SelectOptions = {
    name?: string,
    modelValue?: any,
    disabled?: boolean,
    size?: 'small'|'large',
    clearable?: boolean,
    multi?: boolean,
    prefix?: any,
    placeholder?: string,
    data?: Array<any>,
    textField?: string,
    valueField?: string,
    filter?: boolean,
    renderOption?: (data: any) => any
    renderSelectedItem?: (data: any) => any
    emptyText?: string
    emptyOption?: any,
    showMax?: TagGroupProps['max']
    max?: number
    status?: 'warning'|'error'
    footer?: any
    header?: any
    triggerRender?: (text: string|VNode|any[]) => any
    valueClosable?: boolean,
    transfer?: boolean,
    align?: 'bottomLeft'|'bottomRight',
    showMore?: boolean,
    maxHeight?: number
    loading?: boolean
    remoteMethod?: (queryStr: any) => void
    debounceTime?: number
    asFormField?: boolean
    defaultLabel?: string | string[]
}

export default defineComponent({
    name: 'Select',
    props: {
        name: {type: String as PropType<SelectOptions['name']>},
        modelValue: {type: [String, Number, Array] as PropType<SelectOptions['modelValue']>, default: undefined},
        disabled: {type: Boolean as PropType<SelectOptions['disabled']>},
        size: {type: String as PropType<SelectOptions['size']>},
        clearable: {type: Boolean as PropType<SelectOptions['clearable']>},
        multi: {type: Boolean as PropType<SelectOptions['multi']>},
        prefix: {type: [String, Number, Object] as PropType<SelectOptions['prefix']>},
        placeholder: {type: String as PropType<SelectOptions['placeholder']>},
        data: {type: Array as PropType<SelectOptions['data']>},
        textField: {type: String as PropType<SelectOptions['textField']>, default: 'label'},
        valueField: {type: String as PropType<SelectOptions['valueField']>, default: 'value'},
        filter: {type: Boolean as PropType<SelectOptions['filter']>},
        renderOption: {type: Function as PropType<SelectOptions['renderOption']>},
        renderSelectedItem: {type: Function as PropType<SelectOptions['renderSelectedItem']>},
        emptyOption: {type: [Object, String] as PropType<SelectOptions['emptyOption']>},
        showMax: {type: Number as PropType<SelectOptions['showMax']>},
        max: {type: Number as PropType<SelectOptions['max']>},
        status: {type: String as PropType<SelectOptions['status']>},
        footer: {type: [String, Object] as PropType<SelectOptions['footer']>},
        header: {type: [String, Object] as PropType<SelectOptions['header']>},
        triggerRender: {type: Function as PropType<SelectOptions['triggerRender']>},
        valueClosable: {type: Boolean as PropType<SelectOptions['valueClosable']>},
        transfer: {type: Boolean as PropType<SelectOptions['transfer']>},
        align: {type: String as PropType<SelectOptions['align']>, default: 'bottomLeft'},
        showMore: {type: Boolean as PropType<SelectOptions['showMore']>},
        maxHeight: {type: Number as PropType<SelectOptions['maxHeight']>},
        loading: {type: Boolean as PropType<SelectOptions['loading']>},
        remoteMethod: {type: Function as PropType<SelectOptions['remoteMethod']>},
        debounceTime: {type: Number as PropType<SelectOptions['debounceTime']>},
        asFormField: {type: Boolean as PropType<SelectOptions['asFormField']>, default: true},
        defaultLabel: {type: [String, Array] as PropType<SelectOptions['defaultLabel']>}
    },
    emits: ['change', 'update:modelValue', 'exceed'],
    setup (props, {emit, slots}) {
        const open = ref(false);
        const align = props.align ?? 'bottomLeft';
        const value = formFieldRef(props, emit, props.modelValue ?? props.multi ? [] : '');
        const allTextNodes: Node[] = [];
        const classList = computed(() => ({
            'cm-select': true,
            'cm-select-disabled': props.disabled,
            [`cm-select-${props.size}`]: props.size,
            'cm-select-clearable': !props.disabled && props.clearable && (`${value.value}`.length !== 0),
            'cm-select-multi': props.multi,
            'cm-select-open': open.value,
            'cm-select-with-prefix': props.prefix,
            [`cm-select-status-${props.status}`]: props.status,
        }));
        const filterWrap = ref();
        const textField = props.textField || 'label';
        const valueField = props.valueField || 'value';

        let initLabels: any[] = [];
        if (props.filter && props.defaultLabel) {
            if (props.multi && props.defaultLabel instanceof Array) {
                props.defaultLabel.forEach((label: string, index: number) => {
                    initLabels.push({ [valueField]: value.value[index], [textField]: label, _checked: true, _show: true });
                });
            } else {
                initLabels = [{ [valueField]: value.value, [textField]: props.defaultLabel, _checked: true, _show: true }];
            }
        }

        let isClickChanging = true;
        const query = ref('');
        // 当单选且有默认label时先禁止查询，后放开
        queueMicrotask(() => {
            isClickChanging = false;
        });

        let dataMap: any = {};
        const emptyOption = {[valueField]: '', [textField]: props.emptyOption, _show: true, emptyOption: true};
        if (props.emptyOption) {
            dataMap[''] = emptyOption;
        }
        const showLabels = ref<any[]>(initLabels);
        let debounceTimer: any = null;

        // 重新构建数据
        function buildData (arr: any[], target: any[]) {
            arr && arr.forEach(item => {
                target.push(item);
                item._show = true;
                dataMap[item[valueField]] = item;
                if (item.items) {
                    buildData(item.items, target);
                }
            });
        }

        // 传入的data变化同步更新
        const newData: ComputedRef<any[]> = computed<any[]>(() => {
            const data = props.data || [];
            dataMap = {};
            const newData: any[] = [];
            if (props.emptyOption) {
                newData.push({ [valueField]: '', [textField]: props.emptyOption, _show: true, emptyOption: true });
            }
            if (initLabels) {
                initLabels.forEach(label => {
                    newData.push({ ...label, _show: true });
                });
            }
            if (data) {
                buildData(data, newData);
            }
            return newData;
        });

        const store = reactive({ list: props.emptyOption ? [emptyOption].concat([...initLabels]) : [].concat([...initLabels]) as any[] });

        watch(() => props.data, () => {
            const val = value.value;
            store.list = newData.value;
            const labels: string[] = [];
            store.list.forEach(item => {
                if (props.multi) {
                    item._checked = val.includes(item[valueField]);
                } else {
                    item._checked = val === item[valueField];
                }
                if (item._checked) {
                    labels.push(item);
                }
            });
            showLabels.value = labels;
        }, { immediate: true });

        // 点击更新setValue 并触发onChange事件
        const onOptionClick = (v: any, option: any) => {
            if (dataMap[v]) {
                if (dataMap[v].group) {
                    return;
                }
            }
            let arr: any[] = unref(showLabels);
            if (props.multi) {
                let val = value.value as any[];
                const index = val.indexOf(v);
                if (index > -1) {
                    val.splice(index, 1);
                    arr.splice(index, 1);
                } else {
                    if (props.max && val.length >= props.max) {
                        emit('exceed');
                        return;
                    }
                    val = [...val];
                    val.push(v);
                    arr.push(option);
                }

                value.value = [...val];
                query.value = '';
                emit('change', val);
            } else {
                isClickChanging = true;
                arr = [option];
                value.value = v;
                Promise.resolve().then(() => {
                    isClickChanging = false;
                });
                query.value = '';
                open.value = false;
                emit('change', v, option);
            }
        };

        // const onVisibleChange = (visible) => {
        //     if (!visible) {
        //         query.value = '';
        //     }
        // };

        watchEffect(() => {
            const arr: any[] = [];
            store.list.forEach(item => {
                if (item._checked) {
                    arr.push({id: item[valueField], title: item[textField]});
                }
            });
            let labels = [];
            if (props.multi) {
                labels = arr.length ? arr : (props.emptyOption ? [{id: '', title: props.emptyOption}] : []);
            } else {
                labels = arr.length ? arr[0].title : (props.emptyOption ? props.emptyOption : '');
            }
            showLabels.value = labels;
        });

        // 清空数据
        const onClear = (e: any) => {
            showLabels.value = [];
            if (props.multi) {
                props.onChange && props.onChange([]);
                value.value = [];
            } else {
                props.onChange && props.onChange('');
                value.value = '';
                query.value = '';
                open.value = false;
            }
        };

        // 过滤查询
        watchEffect(() => {
            const queryStr = query.value;
            // 远程查询
            if (props.remoteMethod) {
                if (isClickChanging) {
                    return;
                }
                if (queryStr) {
                    initLabels = [];
                    clearTimeout(debounceTimer);
                    debounceTimer = setTimeout(() => {
                        props.remoteMethod?.(queryStr);
                        // 保持打开状态
                        open.value = true;
                    }, props.debounceTime || 300);
                }
            } else { // 本地过滤
                store.list.forEach(item => {
                    item._show = item[textField].indexOf(queryStr) > -1;
                });
                // 高亮搜索字符
                queueMicrotask(() => {
                    buildNodes();
                    hilightKeyword(queryStr as string);
                });
            }
        });

        /**
     * 构建搜索的节点
     * @returns
     */
        const buildNodes = () => {
        // 不支持高亮则返回
            if (!CSS.highlights) {
                return;
            }

            const treeWalker = document.createTreeWalker(filterWrap.value, NodeFilter.SHOW_TEXT);
            let currentNode = treeWalker.nextNode();
            while (currentNode) {
                allTextNodes.push(currentNode);
                currentNode = treeWalker.nextNode();
            }
        };

        // 高亮关键字
        const hilightKeyword = (queryStr: string) => {
        // 不支持高亮则返回
            if (!CSS.highlights) {
                return;
            }

            CSS.highlights.delete('cm-search-results');

            const str = queryStr.trim().toLowerCase();
            if (!str) {
                return;
            }

            const ranges = allTextNodes
                .map((el) => {
                    return { el, text: el.textContent?.toLowerCase() };
                })
                .map(({ text, el }) => {
                    const indices = [];
                    let startPos = 0;
                    while (text && startPos < text.length) {
                        const index = text.indexOf(str, startPos);
                        if (index === -1) break;
                        indices.push(index);
                        startPos = index + str.length;
                    }
                    return indices.map((index) => {
                        const range = new Range();
                        range.setStart(el, index);
                        range.setEnd(el, index + str.length);
                        return range;
                    });
                });

            const searchResultsHighlight = new window.Highlight(...ranges.flat());
            CSS.highlights.set('cm-search-results', searchResultsHighlight);
        };

        // 多选场景下删除value
        const onValueClose = (item: TagConfig, e: any) => {
            if (props.multi) {
                const arr: any[] = showLabels.value;
                const val = value.value as any[];
                const index = val.indexOf(item.id);
                if (index > -1) {
                    val.splice(index, 1);
                    arr.splice(index, 1);
                }
                value.value = [...val];
                showLabels.value = [...arr];
                emit('change', val);
            }
        };

        // 撤消按键，删除最后一个value
        const onDeleteLastValue = () => {
            if (props.multi) {
                const arr: any[] = showLabels.value;
                const val = value.value as any[];

                if (val.length > 0) {
                    val.pop();
                    arr.pop();
                    value.value = [...val];
                    showLabels.value = [...arr];
                    emit('change', val);
                }
            }
        };

        const displayItems = computed(() => {
            return store.list.filter(item => item._show);
        });

        provide('CMSelectContext', {
            addOption: (option) => {
                option._show = true;
                dataMap[option[valueField]] = option;
                store.list.push(option);
            },
            removeOption: (option) => {
                delete dataMap[option[valueField]];
                store.list = store.list.filter(item => item[valueField] !== option[valueField]);
            }
        });

        onMounted(() => {
            // 将选中的数据同步至store的数据项中
            watchEffect(() => {
                const val = value.value;
                store.list.forEach(item => {
                    if (props.multi) {
                        item._checked = val.includes(item[valueField]);
                    } else {
                        item._checked = val === item[valueField];
                    }
                });
            });
        });

        return () => <div class={classList.value}>
            {
                slots.default?.()
            }
            <Dropdown transfer={props.transfer} align={align} disabled={props.disabled} trigger="click" v-model={open.value}
                menu={<div class="cm-select-options-wrap">
                    {
                        props.header
                            ? <div class="cm-select-header">{props.header}</div>
                            : null
                    }
                    <div class="cm-select-options" style={{'max-height': props.maxHeight ? `${props.maxHeight}px` : ''}}>
                        {
                            props.loading ? <div class="cm-select-loading">加载中</div>
                                : <ul class="cm-select-option-list" ref={filterWrap}>
                                    <VirtualList items={displayItems.value} itemEstimatedSize={30} maxHeight={200}>
                                        {{
                                            default: scope => {
                                                return <VirOption textField={textField} valueField={valueField} value={value.value}
                                                    onClear={onClear} onOptionClick={onOptionClick} renderOption={props.renderOption}
                                                    item={scope.item} index={scope.index} />;
                                            }
                                        }}
                                    </VirtualList>
                                </ul>
                        }
                    </div>
                    {
                        props.footer
                            ? <div class="cm-select-footer">{props.footer}</div>
                            : null
                    }
                </div>}>
                {
                    props.triggerRender
                        ? <span class="cm-select-trigger">{props.triggerRender?.(showLabels.value)}</span>
                        : <Value text={showLabels.value} multi={props.multi} showMax={props.showMax} disabled={props.disabled} showMore={props.showMore}
                            valueClosable={props.valueClosable || (props.filter)} clearable={props.clearable} onClear={onClear} placeholder={props.placeholder}
                            prepend={props.prefix} size={props.size} icon={<FeatherChevronDown class="cm-select-cert"/>} onClose={onValueClose}
                            query={query} filter={props.filter} onDeleteLastValue={onDeleteLastValue}/>
                }
            </Dropdown>
        </div>;
    }
});

function VirOption (props) {
    const item = props.item;
    if (item.emptyOption) {
        return <EmptyOption data={{label: item[props.textField], value: ''}} checked={props.value === ''} onClick={props.onClear}/>;
    } else {
        return <InnerOption key={item[props.valueField]} renderOption={props.renderOption} visible={item._show} disabled={item.disabled} data={item} checked={item._checked}
            textField={props.textField} valueField={props.valueField} onClick={props.onOptionClick}></InnerOption>;
    }
}
VirOption.displayName = 'VirOption';
